/*
 * Copyright (C) 2010 ST-Ericsson AS
 * Author: Dmitry Tarnyagin / dmitry.tarnyagin@stericsson.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/if.h>
#include <linux/sockios.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <fcntl.h>

#ifdef HAVE_ANDROID_OS
#include "cutils/misc.h"
#include "cutils/properties.h"
#endif /* HAVE_ANDROID_OS */

#include "mid_seq.h"
#include "mid_log.h"
#include "mid_sys.h"
#include "mid_power.h"
#include "mid_statemachine.h"

/* from system/core/init/builtins.c */
extern int init_module(void *, unsigned long, const char *);
extern int delete_module(const char *, unsigned int);

#ifndef count_of
#define count_of(x) ((size_t)(sizeof(x) / sizeof((x)[0])))
#endif

/* Common whitespaces */
static const char *tok_ws = "\t\n\r ";

/* Command handler prototype */
typedef int (*cmd_handler_t)(struct mid *mid_info,
			     const struct seq_t *cmd,
			     const struct seq_t *arg);

/* Command entry, associates a handler to a command */
struct cmd_entry_t {
	const char *cmd;
	size_t cmd_len;
	cmd_handler_t handler;
};

#define CMD_ENTRY(c, h) {(c), sizeof(c) - 1, (h)}

/* Whitelist entry, defines an allowed string */
struct whitelist_entry_t {
	const char *cmd;
	size_t cmd_len;
};

#define WHITELIST_ENTRY(c) {(c), sizeof(c) - 1}

/* ------------------------------------------------------------------------ */

/* Command handlers */
static int handle_insmod(struct mid *mid_info, const struct seq_t *cmd, const struct seq_t *arg);
static int handle_rmmod(struct mid *mid_info, const struct seq_t *cmd, const struct seq_t *arg);
static int handle_ifconfig(struct mid *mid_info, const struct seq_t *cmd, const struct seq_t *arg);
static int handle_stop(struct mid *mid_info, const struct seq_t *cmd, const struct seq_t *arg);
static int handle_start(struct mid *mid_info, const struct seq_t *cmd, const struct seq_t *arg);
static int handle_echo(struct mid *mid_info, const struct seq_t *cmd, const struct seq_t *arg);
static int handle_sleep(struct mid *mid_info, const struct seq_t *cmd, const struct seq_t *arg);
static int handle_mfa(struct mid *mid_info, const struct seq_t *cmd, const struct seq_t *arg);
static int handle_killall(struct mid *mid_info, const struct seq_t *cmd, const struct seq_t *arg);
#ifdef MOTOROLA_FEATURE
static int handle_smu(struct mid *mid_info, const struct seq_t *cmd, const struct seq_t *arg);
#endif

/* Definition of supported commands */
static const struct cmd_entry_t cmd_whitelist[] = {
	CMD_ENTRY("insmod",		handle_insmod),
	CMD_ENTRY("rmmod",		handle_rmmod),
	CMD_ENTRY("ifconfig",		handle_ifconfig),
	CMD_ENTRY("stop",		handle_stop),
	CMD_ENTRY("start",		handle_start),
	CMD_ENTRY("echo",		handle_echo),
	CMD_ENTRY("sleep",		handle_sleep),
	CMD_ENTRY("/system/bin/mfa",	handle_mfa),
	CMD_ENTRY("mfa",		handle_mfa),
	CMD_ENTRY("killall",		handle_killall),
#ifdef MOTOROLA_FEATURE
	CMD_ENTRY("/system/bin/smu",	handle_smu),
#endif
};

/* "insmod" command whitelist */
static const struct whitelist_entry_t insmod_whitelist[] =
{
	WHITELIST_ENTRY("/system/lib/modules/hsi_char.ko"),
	WHITELIST_ENTRY("/system/lib/modules/caif_hsi.ko"),
	WHITELIST_ENTRY("/system/lib/modules/hsi_omap.ko"),
        WHITELIST_ENTRY("/system/lib/modules/caif.ko"),
	WHITELIST_ENTRY("/vendor/lib/modules/hsi_char.ko"),
	WHITELIST_ENTRY("/system/lib/modules/hsi_char.ko"),
        WHITELIST_ENTRY("/system/lib/modules/chnl_net.ko"),
  	WHITELIST_ENTRY("/system/lib/modules/caif_socket.ko"),
};

/* "rmmod" command whitelist */
static const struct whitelist_entry_t rmmod_whitelist[] =
{
	WHITELIST_ENTRY("hsi_char"),
	WHITELIST_ENTRY("caif_hsi"),
	WHITELIST_ENTRY("hsi_omap"),
};

/* "ifconfig" command whitelist */
static const struct whitelist_entry_t ifconfig_whitelist[] =
{
	WHITELIST_ENTRY("cfhsi0"),
};

static const struct whitelist_entry_t ifop_whitelist[] =
{
	WHITELIST_ENTRY("up"),
	WHITELIST_ENTRY("down"),
};
#define IFOP_UP		(0)
#define IFOP_DOWN	(1)

/* "start" and "stop" command whitelist */
static const struct whitelist_entry_t start_stop_whitelist[] =
{
	WHITELIST_ENTRY("rfm"),
	WHITELIST_ENTRY("itp-proxy"),
	WHITELIST_ENTRY("ril-daemon"),
#ifdef MOTOROLA_FEATURE
	WHITELIST_ENTRY("atrelay-itp"),
	WHITELIST_ENTRY("atrelay-svc"),
	WHITELIST_ENTRY("atrelay-sig"),
	WHITELIST_ENTRY("mld"),
	WHITELIST_ENTRY("mfa"),

#endif
};

/* "echo" command whitelist */
static const struct whitelist_entry_t echo_whitelist[] =
{
	WHITELIST_ENTRY("ITP > /sys/power/wake_lock"),
	WHITELIST_ENTRY("ITP > /sys/power/wake_unlock"),
};
#define WAKEOP_LOCK	(0)
#define WAKEOP_UNLOCK	(1)

/* ------------------------------------------------------------------------ */

/* Reports whitelist violation */
static void whitelist_violation(const struct seq_t *cmd,
				const struct seq_t *arg)
{
	MLGE("SYS: Command is not allowed: %.*s %.*s\n",
			(int)seq_len(cmd), seq_str(cmd),
			(int)seq_len(arg), seq_str(arg));
}

/* Checks if passed string is whitelisted */
static int check_whitelist(struct seq_t *seq,
			   const struct whitelist_entry_t *whitelist,
			   size_t whitelist_size,
			   size_t *index)
{
	size_t i;
	size_t len = seq_len(seq);
	for (i = 0; i < whitelist_size; ++i) {
		if (len == whitelist[i].cmd_len &&
			!memcmp(seq_str(seq), whitelist[i].cmd, len)) {
			/* Override incoming data with whitelist copy */
			seq->seq = whitelist[i].cmd;
			seq->end = &seq->seq[whitelist[i].cmd_len];
			if (index)
				*index = i;
			return 0;
		}
	}
	return -EINVAL;
}

#ifdef MOTOROLA_FEATURE
static int fork_n_exec(pid_t *pidptr, const struct seq_t *cmd, const struct seq_t *arg)
{
	pid_t child_pid;
	int i, child_exit, arg_cnt, ret;
	struct seq_t args, next;

	MLGD("MID fork and exec cmd: \"%.*s : %.*s\"", (int)seq_len(cmd),
		seq_str(cmd), (int)seq_len(arg), seq_str(arg));

	/* Count the arguments. Start from 1 to account for executable path */
	arg_cnt = 1;
	args = *arg;
	seq_triml(&args);
	seq_trimr(&args);
	while (!seq_empty(&args))
	{
		arg_cnt++;
		seq_tok(&args, &next, tok_ws);
		args = next;
		seq_triml(&args);
	}

	/* Add one more for the NULL terminator */
	char *arg_list[arg_cnt + 1];

	/* Build the args list */
	arg_list[0] = malloc((seq_len(cmd) + 1) * sizeof(char));
	if (!arg_list[0]) {
		MLGE("Failed to allocate command line argument");
		ret = -ENOMEM;
		goto clean_up;
	}
	seq_copyto(cmd, arg_list[0], (seq_len(cmd) + 1));
	MLGE("fork_n_exec arg_list[0]=\"%s\"", arg_list[0]);
	args = *arg;
	seq_triml(&args);
	seq_trimr(&args);
	for (i = 1; i < arg_cnt; i++) {
		seq_tok(&args, &next, tok_ws);
		seq_trimr(&args);
		arg_list[i] = malloc((seq_len(&args) + 1) * sizeof(char));
		if (!arg_list[i]) {
			MLGE("Failed to allocate command line argument");
			ret = -ENOMEM;
			goto clean_up;
		}
		seq_copyto(&args, arg_list[i], (seq_len(&args) + 1));
		MLGE("fork_n_exec arg_list[%d]=\"%s\"", i, arg_list[i]);
		args = next;
		seq_triml(&args);
	}
	arg_list[i] = (char *)NULL;

	/* fork and exec the provided command */
	*pidptr = fork();
	if (*pidptr == 0) {
		ret = execv(arg_list[0], arg_list);
		ret = errno;
		MLGE("Failure on execv %.*s: returned value 0x%X, strerror translated: %s", (int)seq_len(cmd), seq_str(cmd), ret, strerror(ret));
		_exit(-1);
	}
	/* wait for the child to exit */
	do {
		child_pid = waitpid(*pidptr, &child_exit, 0);
	} while (child_pid == -1 && errno == EINTR);
	*pidptr = -1;
	/* setup the correct result to return */
	if (child_pid == -1) {
		ret = child_pid;
	} else {
		if (WIFEXITED(child_exit))
			ret = child_exit;
		else
			ret = -1;
	}
clean_up:
	/* Free the args list */
	for (i = 0; i < arg_cnt; i++)
		if (arg_list[i])
			free(arg_list[i]);
	MLGD("MID fork and exec cmd: \"%.*s %.*s\" returning %d",
		(int)seq_len(cmd), seq_str(cmd), (int)seq_len(arg),
		seq_str(arg), ret);
	return ret;
}
#endif

/* Executes a single command */
static int exec_cmd(struct mid *mid_info, struct seq_t *cmd, struct seq_t *arg)
{
	size_t cmd_len = seq_len(cmd);
	size_t i;

	for (i = 0; i < count_of(cmd_whitelist); ++i) {
		if (cmd_whitelist[i].cmd_len == cmd_len &&
				!memcmp(cmd_whitelist[i].cmd,
					seq_str(cmd), cmd_len)) {
			/* Replace command by whitelisted copy */
			cmd->seq = cmd_whitelist[i].cmd;
			cmd->end = &cmd->seq[cmd_whitelist[i].cmd_len];
			return cmd_whitelist[i].handler(mid_info, cmd, arg);
		}
	}
	whitelist_violation(cmd, arg);
	return -EINVAL;
}

/* Executes sequence of commands like "system" syscall */
int my_system(struct mid *mid_info, const char *cmd_string)
{
	int ret = 0;
	struct seq_t cmd = {cmd_string, cmd_string + (cmd_string?strlen(cmd_string):0)};

	if (cmd_string == NULL)
		return 0;

	while (!ret && !seq_empty(&cmd)) {
		static const char *tok_cmd = "&;";
		struct seq_t next, arg;

		seq_tok(&cmd, &next, tok_cmd);
		if (!seq_empty(&next) &&
				seq_str(&next)[-1] == '&' &&
				seq_str(&next)[0] == '&')
			++next.seq;
		seq_triml(&cmd);
		seq_trimr(&cmd);

		if (!seq_empty(&cmd)) {
			seq_tok(&cmd, &arg, tok_ws);
			seq_triml(&arg);

			ret = exec_cmd(mid_info, &cmd, &arg);
			if (ret && seq_str(&next)[-1] == ';') {
				MLGD("SYS: Ignoring retcode for %.*s %.*s\n",
					(int)seq_len(&cmd), seq_str(&cmd),
					(int)seq_len(&arg), seq_str(&arg));
				ret = 0;
			}
		}

		cmd = next;
	}
	return ret;
}

/* ------------------------------------------------------------------------ */
/* Command handlers */

static int insmod(const char *filename, const char *args)
{
#ifdef HAVE_ANDROID_OS
	void *module;
	unsigned int size;
	int ret;

	module = load_file(filename, &size);
	if (!module)
		return -1;

	ret = init_module(module, size, args);

	free(module);

	return ret;
#else /* HAVE_ANDROID_OS */
	MLGD("SYS: %s command is not implemented.\n",
			"insmod");
	return -EINVAL;
#endif /* HAVE_ANDROID_OS */
}

static int handle_insmod(struct mid *mid_info, const struct seq_t *cmd, const struct seq_t *arg)
{
	int ret;
	char filename[256], args[128];
	struct seq_t module = *arg;
	struct seq_t modarg;
	UNUSED(mid_info);

	seq_tok(&module, &modarg, tok_ws);
	if (check_whitelist(&module, insmod_whitelist,
			count_of(insmod_whitelist), NULL)) {
		whitelist_violation(cmd, arg);
		return -EINVAL;
	}
	if (seq_copyto(&module, filename, sizeof(filename)) ||
			seq_copyto(&modarg, args, sizeof(args))) {
		whitelist_violation(cmd, arg);
		return -EINVAL;
	}

	MLGD("SYS: Executing %.*s: %.*s: %.*s\n",
			(int)seq_len(cmd), seq_str(cmd),
			(int)seq_len(&module), seq_str(&module),
			(int)seq_len(&modarg), seq_str(&modarg));

	ret = insmod(filename, args);
	if (ret) {
		if(errno == EEXIST) {
			MLGW("Module already loaded: %s", filename);
			ret = 0;
		} else {

		MLGE("SYS: %.*s for %.*s  %.*s failed: %s.\n",
			(int)seq_len(cmd), seq_str(cmd),
			(int)seq_len(&module), seq_str(&module),
			(int)seq_len(&modarg), seq_str(&modarg),
			strerror(errno));
		}
	}

	return ret;
}

static int rmmod(const char *modname)
{
	int ret = -1;
	int maxtry = 10;

	while (maxtry-- > 0) {
		ret = delete_module(modname, O_NONBLOCK | O_EXCL);
		if (ret < 0 && errno == EAGAIN)
			usleep(500000);
		else
			break;
	}

	if (ret != 0)
		MLGD("Unable to unload driver module \"%s\": %s\n",
				modname, strerror(errno));
	return ret;
}

static int handle_rmmod(struct mid *mid_info, const struct seq_t *cmd, const struct seq_t *arg)
{
	int ret;
	char filename[128];
	struct seq_t module = *arg;
	UNUSED(mid_info);

	if (check_whitelist(&module, rmmod_whitelist,
			count_of(rmmod_whitelist), NULL)) {
		whitelist_violation(cmd, arg);
		return -EINVAL;
	}
	if (seq_copyto(&module, filename, sizeof(filename))) {
		whitelist_violation(cmd, arg);
		return -EINVAL;
	}

	MLGD("SYS: Executing %.*s: %.*s\n",
			(int)seq_len(cmd), seq_str(cmd),
			(int)seq_len(&module), seq_str(&module));

	ret = rmmod(filename);
	if (ret) {
		MLGE("SYS: %.*s for %.*s failed: %s.\n",
			(int)seq_len(cmd), seq_str(cmd),
			(int)seq_len(&module), seq_str(&module),
			strerror(errno));
	}

	return 0;
}

static int setflags(int s, struct ifreq *ifr, int set, int clr)
{
	if (ioctl(s, SIOCGIFFLAGS, ifr) < 0)
		return -EINVAL;
	ifr->ifr_flags = (ifr->ifr_flags & (~clr)) | set;
	if (ioctl(s, SIOCSIFFLAGS, ifr) < 0)
		return -EINVAL;
	return 0;
}

static int handle_ifconfig(struct mid *mid_info, const struct seq_t *cmd, const struct seq_t *arg)
{
	int s;
	int ret = -EINVAL;
	struct ifreq ifr;
	struct seq_t iface = *arg;
	struct seq_t ifop;
	size_t index;
	UNUSED(mid_info);

	memset(&ifr, 0, sizeof(struct ifreq));
	seq_tok(&iface, &ifop, tok_ws);
	if (check_whitelist(&iface, ifconfig_whitelist,
			count_of(ifconfig_whitelist), NULL)) {
		whitelist_violation(cmd, arg);
		return -EINVAL;
	}
	if (check_whitelist(&ifop, ifop_whitelist,
			count_of(ifop_whitelist), &index)) {
		whitelist_violation(cmd, arg);
		return -EINVAL;
	}
	if (seq_copyto(&iface, ifr.ifr_name, sizeof(ifr.ifr_name))) {
		whitelist_violation(cmd, arg);
		return -EINVAL;
	}

	MLGD("SYS: Executing %.*s: %.*s: %.*s\n",
			(int)seq_len(cmd), seq_str(cmd),
			(int)seq_len(&iface), seq_str(&iface),
			(int)seq_len(&ifop), seq_str(&ifop));


	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
		MLGE("SYS: Unable to open control socket: %s.\n",
				strerror(errno));
		return -ENOMEM;
	}

	switch (index) {
	case IFOP_UP:
		ret = setflags(s, &ifr, IFF_UP, 0);
		break;
	case IFOP_DOWN:
		ret = setflags(s, &ifr, 0, IFF_UP);
		break;
	default:
		MLGE("SYS: Unsupported interface flag.");
		break;
	}

	if (ret) {
		MLGE("SYS: %.*s %.*s for %.*s failed: %s.\n",
			(int)seq_len(cmd), seq_str(cmd),
			(int)seq_len(&ifop), seq_str(&ifop),
			(int)seq_len(&iface), seq_str(&iface),
			strerror(errno));
	}
	close(s);
	return ret;
}

static int handle_stop(struct mid *mid_info, const struct seq_t *cmd, const struct seq_t *arg)
{
#ifdef HAVE_ANDROID_OS
	char name[128];
	struct seq_t service = *arg;
	UNUSED(mid_info);

	if (check_whitelist(&service, start_stop_whitelist,
			count_of(start_stop_whitelist), NULL)) {
		whitelist_violation(cmd, arg);
		return -EINVAL;
	}
	if (seq_copyto(&service, name, sizeof(name))) {
		whitelist_violation(cmd, arg);
		return -EINVAL;
	}

	MLGD("SYS: Executing %.*s: %.*s\n",
			(int)seq_len(cmd), seq_str(cmd),
			(int)seq_len(&service), seq_str(&service));

	property_set("ctl.stop", name);
	return 0;
#else
	MLGD("SYS: %s command is not implemented.\n",
			"stop");
	return -EINVAL;
#endif
}

static int handle_start(struct mid *mid_info, const struct seq_t *cmd, const struct seq_t *arg)
{
	char name[128];
	struct seq_t service = *arg;
	UNUSED(mid_info);

	if (check_whitelist(&service, start_stop_whitelist,
			count_of(start_stop_whitelist), NULL)) {
		whitelist_violation(cmd, arg);
		return -EINVAL;
	}
	if (seq_copyto(&service, name, sizeof(name))) {
		whitelist_violation(cmd, arg);
		return -EINVAL;
	}

	MLGD("SYS: Executing %.*s: %.*s\n",
			(int)seq_len(cmd), seq_str(cmd),
			(int)seq_len(&service), seq_str(&service));

#ifdef HAVE_ANDROID_OS
	property_set("ctl.start", name);
#endif
	return 0;
}

static int handle_echo(struct mid *mid_info, const struct seq_t *cmd, const struct seq_t *arg)
{
	int ret = -EINVAL;
	struct seq_t what = *arg;
	size_t index;

	if (check_whitelist(&what, echo_whitelist,
			count_of(echo_whitelist), &index)) {
		whitelist_violation(cmd, arg);
		return -EINVAL;
	}

	MLGD("SYS: Executing %.*s: %.*s\n",
			(int)seq_len(cmd), seq_str(cmd),
			(int)seq_len(&what), seq_str(&what));

#ifdef HAVE_ANDROID_OS
	switch (index) {
	case WAKEOP_LOCK:
		ret = mid_set_wakelock(mid_info->mid_power, "ITP", 1);
		break;
	case WAKEOP_UNLOCK:
		ret = mid_set_wakelock(mid_info->mid_power, "ITP", 0);
		break;
	}
#else /* HAVE_ANDROID_OS */
	MLGD("SYS: %s command is not implemented.\n",
			"echo");
#endif /* HAVE_ANDROID_OS */

	if (ret) {
		MLGE("SYS: %.*s %.*s failed: %s.\n",
			(int)seq_len(cmd), seq_str(cmd),
			(int)seq_len(&what), seq_str(&what),
			strerror(errno));
	}
	return ret;
}

static int handle_sleep(struct mid *mid_info, const struct seq_t *cmd, const struct seq_t *arg)
{
	char buf[10];
	char *end = NULL;
	long val;
	UNUSED(mid_info);

	if (seq_copyto(arg, buf, sizeof(buf))) {
		whitelist_violation(cmd, arg);
		return -EINVAL;
	}

	val = strtol(buf, &end, 0);
	if (!end || *end) {
		whitelist_violation(cmd, arg);
		return -EINVAL;
	}

	MLGD("SYS: Executing %.*s: %d",
			(int)seq_len(cmd), seq_str(cmd),
			(int)val);
	sleep(val);
	return 0;
}

static bool logging_system(const char* command)
{
	bool success = false;
	int status = system(command);
	if (status == -1) {
		MLGE("SYS; Failed to run command \"%s\": %s", command, strerror(errno));
	} else if(WIFEXITED(status)) {
		// POSIX exit status handling. See section 2.8.2 of
		// http://pubs.opengroup.org/onlinepubs/000095399/utilities/xcu_chap02.html
		int exit_status=WEXITSTATUS(status);
		if (exit_status == 0) {
			success = true;
		} else if (exit_status == 126) {
			MLGE("Command \"%s\" not executable", command);
		} else if (exit_status == 127) {
			MLGE("SYS: Command \"%s\" not found", command);
		} else if (exit_status > 127) {
			if (exit_status < 128+SIGRTMAX) {
				MLGE("SYS: Command \"%s\" terminated on signal %d", command, exit_status - 128);
			} else {
				// Grandfather in incorrect "exit(-1)" used by
				// the Android toolbox commands. See
				// http://code.google.com/p/android/issues/detail?id=17246
				MLGE("SYS: Command \"%s\" failed with exit code %d (-%d)", command, exit_status, 256-exit_status);
			}
		} else {
			MLGE("SYS: Command \"%s\" failed with exit code %d", command, exit_status);
		}
	} else if(WIFSIGNALED(status)) {
		MLGE("SYS: Shell of command \"%s\" was terminated on signal %d", command, WTERMSIG(status));
	} else {
		MLGE("SYS: Command \"%s\" terminated for an unknown reason: %08x", command, status);
	}
	return success;
}

static int handle_mfa(struct mid *mid_info __attribute__((unused)), const struct seq_t *cmd, const struct seq_t *arg)
{
	size_t index;
	char command[200];

	seq_copyto(cmd, command, sizeof(command));
	index=strlen(command);
	snprintf(command+index, sizeof(command)-index, " ");
	index++;
	seq_copyto(arg, command+index, sizeof(command)-index);

	return logging_system(command)?0:-1;
}
#if defined(MOTOROLA_FEATURE)
static int handle_smu(struct mid *mid_info __attribute__((unused)), const struct seq_t *cmd, const struct seq_t *arg)
{
	pid_t pid;
	bool success = false;
	int status;

	status = fork_n_exec(&pid, cmd, arg);
	if (status == -1) {
		MLGE("SYS; Failed to run command \"%.*s\": %s",
			(int)seq_len(cmd), seq_str(cmd), strerror(errno));
	} else if(WIFEXITED(status)) {
		// POSIX exit status handling. See section 2.8.2 of
		// http://pubs.opengroup.org/onlinepubs/000095399/utilities/xcu_chap02.html
		int exit_status=WEXITSTATUS(status);
		if (exit_status == 0) {
			success = true;
		} else if (exit_status == 126) {
			MLGE("Command \"%.*s\" not executable",
				(int)seq_len(cmd), seq_str(cmd));
		} else if (exit_status == 127) {
			MLGE("SYS: Command \"%.*s\" not found",
				(int)seq_len(cmd), seq_str(cmd));
		} else if (exit_status > 127) {
			if (exit_status < 128+SIGRTMAX) {
				MLGE("SYS: Command \"%.*s\" terminated on signal %d",
					(int)seq_len(cmd), seq_str(cmd),
					exit_status - 128);
			} else {
				// Grandfather in incorrect "exit(-1)" used by
				// the Android toolbox commands. See
				// http://code.google.com/p/android/issues/detail?id=17246
				MLGE("SYS: Command \"%.*s\" failed with exit code %d (-%d)",
					(int)seq_len(cmd), seq_str(cmd),
					exit_status, 256-exit_status);
			}
		} else {
			MLGE("SYS: Command \"%.*s\" failed with exit code %d",
				(int)seq_len(cmd), seq_str(cmd), exit_status);
		}
	} else if(WIFSIGNALED(status)) {
		MLGE("SYS: Shell of command \"%.*s\" was terminated on signal %d",
			(int)seq_len(cmd), seq_str(cmd), WTERMSIG(status));
	} else {
		MLGE("SYS: Command \"%s\" terminated for an unknown reason: %08x",
			(int)seq_len(cmd), seq_str(cmd), status);
	}
	return success?0:-1;
}
#endif

static int handle_killall(struct mid *mid_info __attribute__((unused)), const struct seq_t *cmd, const struct seq_t *arg)
{
	size_t index;
	char command[200];

	seq_copyto(cmd, command, sizeof(command));
	index=strlen(command);
	snprintf(command+index, sizeof(command)-index, " ");
	index++;
	seq_copyto(arg, command+index, sizeof(command)-index);

	return logging_system(command)?0:-1;
}
#if 0
int main(int argc, char **argv)
{
	char *strings[] = {
		strdup("insmod /system/lib/modules/kernel/drivers/staging/omap_hsi/hsi_char.ko channels_map=1;"),
		strdup("rmmod hsi_char;"),
		strdup("stop ril-daemon && start itp-proxy && echo ITP > /sys/power/wake_lock"),
		strdup("ifconfig cfhsi0 down; rmmod hsi_omap; rmmod caif_hsi; rmmod hsi_char;"),
		strdup("stop itp-proxy && echo ITP > /sys/power/wake_unlock"),
		strdup("sleep 5; start rfm; insmod /system/lib/modules/kernel/drivers/staging/omap_hsi/hsi_char.ko channels_map=1;"),
		strdup("insmod /system/lib/modules/kernel/drivers/net/caif/caif_hsi.ko &&\n\
                          insmod /system/lib/modules/kernel/drivers/net/caif/phy/hsi_omap.ko sw_reset_on_cfhsi_up=1 && ifconfig cfhsi0 up;"),
		strdup("stop rfm; ifconfig cfhsi0 down; rmmod hsi_omap; rmmod caif_hsi; rmmod hsi_char;"),
		strdup("stop itp-proxy; echo ITP > /sys/power/wake_unlock ;stop rfm; ifconfig cfhsi0 down; rmmod hsi_omap; rmmod caif_hsi;"),
		NULL
	};
	int i;

	for(i = 0; strings[i]; ++i)
		mid_command(strings[i]);

	return 0;
}
#endif
